/**
 * @(#)Paillier.java
 *
 *
 * @author 
 * @version 1.00 2011/4/26
 */
 
package Paillier;

import java.math.*;
import java.util.*;

public class Paillier 
{
	public BigInteger p;
	public BigInteger q;
	public BigInteger n;
	public BigInteger np2;
	public BigInteger mue;
	public BigInteger g;
	public BigInteger lamda;
	public BigInteger glMnp2;
	
	public void getKeys()
	{
		int nums[] = null;
		PrimeNum pn = PrimeNum.newPrimeNum();
		nums = pn.getNums();
		p = new BigInteger(nums[0]+"");
		q = new BigInteger(nums[1]+"");
		n = p.multiply(q);
		np2 = n.multiply(n);
		lamda = lcm(p.subtract(BigInteger.ONE),q.subtract(BigInteger.ONE));
		g = new BigInteger(np2.bitLength(),new Random());/////////////////
		glMnp2 = g.modPow(lamda, np2);
		BigInteger glMnp2S1 = glMnp2.subtract(BigInteger.ONE);
		BigInteger glMnp2S1Dn = glMnp2S1.divide(n);
		BigInteger gcd = glMnp2S1Dn.gcd(n);
		if(gcd.intValue()!=1)
		{
			try {
				throw new Exception("Erroooooooooorrrrrr");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		BigInteger l = l(glMnp2,n);
		mue = l.modInverse(n);
	}
	
	private BigInteger lcm(BigInteger num1,BigInteger num2)
	{
		BigInteger mul = num1.multiply(num2);
		BigInteger gcd = num1.gcd(num2);
		return mul.divide(gcd);
	}
		
	private BigInteger l(BigInteger u,BigInteger n)
	{
		BigInteger u1 = u.subtract(BigInteger.ONE);
		return u1.divide(n);
	}
	
	private BigInteger Encrypt(BigInteger b)
	{
		BigInteger r = new BigInteger(n.bitLength(),new Random());
		BigInteger gm = g.modPow(b, np2);
		BigInteger rn = r.modPow(n, np2); 
		BigInteger C = gm.multiply(rn).mod(np2);
		return C;
	}
	
	private String Encrypt(String message)
	{
		String s = "";
		for(int i =0;i<message.length();i++)
		{
			s += Encrypt(new BigInteger((int) message.charAt(i)+""));
			s += " ";
		}
		return s;
	}
	
	public BigInteger EncryptByNG(BigInteger b,BigInteger n,BigInteger g)
	{
		BigInteger r = new BigInteger(n.bitLength(),new Random());
		BigInteger np2 = n.multiply(n);
		BigInteger gm = g.modPow(b, np2);
		BigInteger rn = r.modPow(n, np2); 
		BigInteger C = gm.multiply(rn).mod(np2);
		return C;
	}
	
	public String EncryptByNG(String message,BigInteger n,BigInteger g)
	{
		String s = "";
		for(int i =0;i<message.length();i++)
		{
			s += EncryptByNG(new BigInteger((int) message.charAt(i)+""),n,g);
			s += " ";
		}
		return s;
	}
	
	public BigInteger Decrypt(BigInteger cipher)
	{
		BigInteger clMn2 = cipher.modPow(lamda, np2);
		BigInteger l = l(clMn2,n);
		BigInteger M = l.multiply(mue).mod(n);
		return M;
	}
	
	public String Decrypt(String cipher)
	{
		String s = "";
		String ciphersplit[] = cipher.split(" ");
		for(int i =0;i<ciphersplit.length;i++)
		{
			s += (char)(Decrypt(new BigInteger(ciphersplit[i])).intValue());
		}
		return s;
	}


	public static void main (String[] args) {
		Paillier p = new Paillier();
		p.getKeys();
		String e1 = p.Encrypt("Heba El Mohandes");
		String e2 = p.Encrypt("Dalia Hegazy");
		String d1 = p.Decrypt(e1);
		String d2 = p.Decrypt(e2);
		System.out.println(e1.toString());
		System.out.println(d1.toString());
		System.out.println(e2.toString());
		System.out.println(d2.toString());
	}
}